/*
 * Created by JFormDesigner on Thu Jan 20 22:35:44 ART 2011
 */

package ar.com.visualAORE.view;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.ResourceBundle;
import javax.swing.*;

import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JTabbedPane;
import javax.swing.JTextPane;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.Element;
import javax.swing.text.Highlighter;
import javax.swing.text.Position;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.View;

import main.ImagePanel2;
import main.MapScale;

import org.jdesktop.swingx.VerticalLayout;

import com.ngs.image.ImageModel;
import com.ngs.image.ImagePanel;
import com.ngs.image.ImageSource;
import com.ngs.image.source.ImageIOSource;

import sl.com.sl.ImageCreator;
import sl.com.sl.StripesImageContainer;
import sl.com.sl.StripesImageDialog;
import sl.com.sl.data.CSExchange;
import sl.com.sl.data.CSExchangeImpl;

import ar.com.visualAORE.controller.ReqInternalFrameController;
import ar.com.visualAORE.model.Document;
import ar.com.visualAORE.model.Selection;
import ar.com.visualAORE.util.TextLineNumber;
import ar.com.visualAORE.util.ToasterMessage;
import ar.com.visualAORE.util.UnderlineHighlightPainter;
import ar.com.visualAORE.view.collapse.CollapsibleEditorKit;
import ar.com.visualAORE.view.collapse.CollapsibleView;
import ar.com.visualAORE.view.panel.MapViewPanel;
import ar.com.visualAORE.view.panel.ProgressBarDialog;
import ar.com.visualAORE.view.panel.TextViewPanel;


/**
 * @author julian
 */
public class ReqInternalFrame extends JInternalFrame {
	private static final long serialVersionUID = 1L;
	private static final int SEPARACION = 24;
	static int inset = 0;
	ReqInternalFrameController controller;
	VisualDesktopFrame vdf;
	Highlighter highlighter;
	Document document = null;
	double scale = 1.0;
	TextLineNumber tln;
	TextViewPanel textView;
	MapViewPanel mapView;
	JScrollPane scrollMap;
	private int zoomValue = 3;
	
	
	public ReqInternalFrame() {
		initComponents();
		this.sliderThreshold.setEnabled(false);
		 setLocation(SEPARACION*inset, SEPARACION*inset);
	        ++inset;
	   // this.tln = new TextLineNumber(this.textPane1);
	}
	public ReqInternalFrame(VisualDesktopFrame vdf, ReqInternalFrameController controller) {
        super(null,true,true,true,true);
        this.controller = controller;
        this.vdf = vdf;
        initComponents();
       // initZoomSlider();
        
        mapView = new MapViewPanel(vdf, this, controller);
        textView = new TextViewPanel(vdf, this, controller, mapView);
        
        this.highlighter = textView.getTextPane().getHighlighter();
        
        int chWidth = (int) vdf.getWidth()*55/100;
        int chHeight = (int) vdf.getHeight()*50/100;
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        setBounds((screenSize.width-chWidth)/2, (screenSize.height-chHeight)/2, chWidth, chHeight);
        setLocation(SEPARACION*inset, SEPARACION*inset);
        ++inset;
        this.mapControls(false);
        this.tabbedPaneConcernThreshold.setEnabledAt(0, true);
        this.contentPanel.add(textView, BorderLayout.CENTER);
        
       }
		
	//Getters
	public Highlighter getHighlighter() {
		return highlighter;
	}
	public void setHighlighter(Highlighter highlighter) {
		this.highlighter = highlighter;
	}
	
	/**
	 * Pinta un requerimiento a partir de su archivo xml
	 * @param document
	 */
	public void applyDocumentLayer(Document document, int threshold){
		this.document = document;
		Iterator<Selection> it = document.getSelections().iterator();
		while(it.hasNext()){
			Selection selection = it.next();
			if(getSelectionPorcent(selection.getSize()) >= threshold){
			//vdf.setPainter(new UnderlineHighlightPainter(selection.getConcern().getColor()));	
				try { 
					//highlighter.addHighlight(selection.getDot(), selection.getMark(), vdf.getPainter());
						highlighter.addHighlight(selection.getDot(), selection.getMark(), new DefaultHighlighter.DefaultHighlightPainter(selection.getConcern().getColor()));
						
					System.out.println(getSelectionPorcent(selection.getSize()));
	
				} catch (BadLocationException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}

	public void goToSpecificPosition(int position){
		textView.getTextPane().setCaretPosition(position); 
		textView.getScrollPane().setViewportView(textView.getTextPane());
		radioTextActionPerformed(null);
	}

	private void sliderThresholdStateChanged(ChangeEvent e) {
		label2.setText(String.valueOf(sliderThreshold.getValue()) + "%");

	}

/*	private void sliderZoomStateChanged(ChangeEvent e) {
		// TODO add your code here
//		 int value = sliderZoom.getValue();
         scale = (value+4)/20.0;
         revalidate();
         repaint();
	}
*/	/*public JSlider getZoomSlider(){
	//	return this.sliderZoom;
	}*/
	/*private void buttonApplyThresoldActionPerformed(ActionEvent e) {
		System.out.println("Thresold");
		highlighter.removeAllHighlights();
		applyDocumentLayer(this.document, sliderThreshold.getValue());
		CSExchange data = new CSExchangeImpl(this.getTextViewPanel().getTextPane());
		StripesImageContainer c = new StripesImageContainer(getDemoImage(data), data.getEditor(), this);
		JScrollPane sp = new JScrollPane(c);
		sp.repaint();
		if(this.document != null){
			this.contentPanel.remove(textView);
			this.contentPanel.add(sp, BorderLayout.CENTER);
			this.contentPanel.repaint();
			this.repaint();
			this.mapControls(true);
			this.textControls(false);
		}
	}
	*/
	public JEditorPane getTextView(){
		return this.textView.getTextPane();
	}
	public TextViewPanel getTextViewPanel(){
		return textView;
	}
	/*public MapViewPanel getMapViewPanel(){
		return mapView;
	}*/
	/*private void initZoomSlider(){
		 int min = -3, max = 3, inc = 1;
		 sliderZoom.setMinimum(min);
		 sliderZoom.setMaximum(max);
		 sliderZoom.setValue(0);
		 sliderZoom.setMajorTickSpacing(5);
		 sliderZoom.setMinorTickSpacing(1);
		 sliderZoom.setPaintTicks(true);
		 sliderZoom.setSnapToTicks(true);
		 sliderZoom.setLabelTable(getLabelTable(min, max, inc));
		 sliderZoom.setPaintLabels(true);
	}*/
/*	 private Hashtable getLabelTable(int min, int max, int inc) {
	        Hashtable<Integer,JLabel> table = new Hashtable<Integer,JLabel>();
	        for(int j = min; j <= max; j += inc) {
	            //String s = String.format("%.2f", (j+4)/20.0);
	            table.put(Integer.valueOf(j), new JLabel(String.valueOf(j)));
	        }
	        return table;
	 }
*/	 

	  /** 
	   * get Line Count 
	   *  
	   * @param str 
	   * @return the count of '\n' in the String 
	   */ 
	/*  public int getLineCount(String str){ 
	      String tempStr1 = new String(str.toLowerCase());
	      String tempStr2 = new String(str.toLowerCase());
	      String tempStr3 = new String(str.toLowerCase());
	      
	      int index; 
	      int lineCount = 0; 
	      while (tempStr1.length() > 0){ 
	    	  index = tempStr1.indexOf("<br>"); 
	    	  if(index != -1){ 
	    		  lineCount++; 
	    		  tempStr1 = tempStr1.substring(index+1); 
	    	  } 
	    	  else{ 
	    		  break; 
	    	  } 
	      } 
	      while (tempStr2.length() > 0){ 
	    	  index = tempStr2.indexOf("<p>"); 
	    	  if(index != -1){ 
	    		  lineCount++; 
	    		  tempStr2 = tempStr2.substring(index+1); 
	    	  } 
	    	  else{ 
	    		  break; 
	    	  } 
	      } 
	      while (tempStr3.length() > 0){ 
	    	  index = tempStr3.indexOf("<p>"); 
	    	  if(index != -1){ 
	    		  lineCount++; 
	    		  tempStr3 = tempStr3.substring(index+1); 
	    	  } 
	    	  else{ 
	    		  break; 
	    	  } 
	      } 
	      return lineCount; 
	   } */
	  
	  //Test
/*	  @Override  
	    public void paint(Graphics g) {  
	        super.paint(g);  
	        final FontMetrics fontMetrics = g.getFontMetrics();  
	        // get width of any char (font is monospaced)  
	        final int charWidth = fontMetrics.charWidth('M');  
	        final int charHeight = fontMetrics.getHeight();  
	        final int prefWidth = this.getPreferredSize().width;  
	        // get height of Parent (JScrollPane)  
	        final int prefHeight = this.getParent().getHeight();  
	         
	        int maxChars = prefWidth/charWidth;  
	        int maxLines = prefHeight/charHeight;  
	    }  
*/	  
	  public void radioTextActionPerformed(ActionEvent e) {
		this.contentPanel.removeAll();
		this.contentPanel.add(textView, BorderLayout.CENTER);
		this.contentPanel.repaint();
		this.repaint();
		this.textControls(true);
		this.mapControls(false);
		
		this.tabbedPaneConcernThreshold.setEnabledAt(0, true);
		this.tabbedPaneConcernThreshold.setEnabledAt(1, false);
		this.buttonApplyThreshold.setVisible(true);
	  }

	  private void radioMapActionPerformed(ActionEvent e) {
		try {
			//this.zoomValue = 3;
			ToasterMessage.showMessage("Creating image, please wait");
			CSExchange data = new CSExchangeImpl(this.getTextViewPanel().getTextPane());
			
			MapScale test = new MapScale(getDemoImage(data));
			
			//StripesImageContainer c = new StripesImageContainer(getDemoImage(data), data.getEditor(), this, zoomValue);
			StripesImageContainer c = new StripesImageContainer(test, data.getEditor(), this);
			//JScrollPane sp = new JScrollPane(c);
			JScrollPane sp = new JScrollPane(test);
			sp.repaint();
			//if(this.document != null){
				System.out.println("DENTRO DEL IF");
				this.contentPanel.removeAll();
				//this.contentPanel.remove(textView);
				this.contentPanel.add(sp, BorderLayout.CENTER);
				this.panel2.removeAll();
				this.panel2.add(test.getSlider());
				//this.getContentPane().add(new JScrollPane(new ImagePanel2(getDemoImage(data), 0.30)));
				//this.getContentPane().add(main.getSlider(), "Last");
		        test.repaint();
		        test.revalidate();
				this.contentPanel.repaint();
				this.repaint();
				this.mapControls(true);
				this.textControls(false);
		//	}
		} catch (OutOfMemoryError e1) {
			// TODO Auto-generated catch block
			JOptionPane.showMessageDialog(this, "Out of Memory! , please increase the Java heap space");
			e1.printStackTrace();
		}
	  }
	  public BufferedImage getDemoImage(CSExchange data) {
		  return ImageCreator.createStripesImage(data);
	  }
	  
	  public JRadioButton getRadioButtonText(){
		  return this.radioText;
	  }
	  
	  //Muestra u oculta los controles para el mapa
	  public void mapControls(boolean flag){
		  //this.buttonApplyThreshold.setVisible(flag);
		  //this.tabbedPaneConcernThreshold.setVisible(flag);
		  this.tabbedPaneConcernThreshold.setSelectedIndex(0);
		  this.tabbedPaneConcernThreshold.setEnabledAt(1, flag);
		  this.radioMap.setSelected(flag);
	  }
	  
	  public void textControls(boolean flag){
		  this.tabbedPaneConcernThreshold.setSelectedIndex(1);
		  this.buttonExport.setVisible(flag);
		  this.labelLineNumber.setVisible(flag);
		  this.radioText.setSelected(flag);
	  }

	  private void buttonzoomOutActionPerformed(ActionEvent e) {
		/*  System.gc(); 
		  try {
			  ToasterMessage.showMessage("Creating image, please wait");
			  StripesImageContainer c;
			  CSExchange data = new CSExchangeImpl(this.getTextViewPanel().getTextPane());
			  this.zoomValue-=1;
			  
			  if(this.zoomValue == 0)
				  c = new StripesImageContainer(ZoomControl.zoomOut(getDemoImage(data), 1), data.getEditor(), this, zoomValue);
			  else{
				  if(this.zoomValue > 0)
					c = new StripesImageContainer(ZoomControl.zoomIn(getDemoImage(data), Math.abs(this.zoomValue)), data.getEditor(), this, zoomValue);
				  else
					c = new StripesImageContainer(ZoomControl.zoomOut(getDemoImage(data), Math.abs(this.zoomValue)), data.getEditor(), this, zoomValue);
				
			  	JScrollPane sp = new JScrollPane(c);
				this.contentPanel.removeAll();
				this.contentPanel.add(sp, BorderLayout.CENTER);
				this.revalidate();
				c.repaint();
			  }
		
		} catch (OutOfMemoryError e1) {
			// TODO Auto-generated catch block
			JOptionPane.showMessageDialog(this, "Out of Memory! , please increase the Java heap space");
			e1.printStackTrace();
		}*/
	  }

	  private void buttonZoomInActionPerformed(ActionEvent e) {
		 /* System.gc();
		  try {
			ToasterMessage.showMessage("Creating image, please wait");
			StripesImageContainer c;
			  CSExchange data = new CSExchangeImpl(this.getTextViewPanel().getTextPane());
			  this.zoomValue+=1;
			  
			  if(this.zoomValue == 0)
				  c = new StripesImageContainer(ZoomControl.zoomIn(getDemoImage(data), 1), data.getEditor(), this, 1);
			  else{
				  if(this.zoomValue < 0)
					c = new StripesImageContainer(ZoomControl.zoomOut(getDemoImage(data), Math.abs(this.zoomValue)), data.getEditor(), this, zoomValue);
				  else
					c = new StripesImageContainer(ZoomControl.zoomIn(getDemoImage(data), Math.abs(this.zoomValue)), data.getEditor(), this, zoomValue);
				
			  	JScrollPane sp = new JScrollPane(c);
				this.contentPanel.removeAll();
				this.contentPanel.add(sp, BorderLayout.CENTER);
				this.revalidate();
				c.repaint();
			}
		} catch (OutOfMemoryError e1) {
			// TODO Auto-generated catch block
			JOptionPane.showMessageDialog(this, "Out of Memory! , please increase the Java heap space");
			e1.printStackTrace();
		}*/
	  }
	  
	  /**
		 * Devuelve el porcentaje con respecto al total del documento, de la
		 * selección que se pasa como parámetro
		 * 
		 * @param size
		 *            tamaño de la selección
		 * @return el porcentaje de la selección
		 */
		public float getSelectionPorcent(int size) {
			float result = (float) (size * 100)
					/ (float) this.document.getDocumentSize();
			return result;
		}
		
		/**
		 * Metodo que se ejecuta cuando se presiona el boton apply threshold
		 * @param e
		 */
		private void buttonApplyThresholdActionPerformed(ActionEvent e) {
			// TODO add your code here
			System.out.println("Thresold");
				highlighter.removeAllHighlights();
				applyDocumentLayer(this.document, sliderThreshold.getValue());
				this.revalidate();
			
			if(radioMap.isSelected()){
				CSExchange data = new CSExchangeImpl(this.getTextViewPanel().getTextPane());
				MapScale test = new MapScale(getDemoImage(data));
				StripesImageContainer c = new StripesImageContainer(test, data.getEditor(), this);
				//StripesImageContainer c = new StripesImageContainer(getDemoImage(data), data.getEditor(), this, zoomValue);
			  	//JScrollPane sp = new JScrollPane(c);
				JScrollPane sp = new JScrollPane(test);
				this.panel2.removeAll();
				this.panel2.add(test.getSlider());
			  	this.contentPanel.removeAll();
				this.contentPanel.add(sp, BorderLayout.CENTER);
				this.revalidate();
				c.repaint();
			}
		}
/*	  public void addBlink(int startPos, int endPos, Color blinkColor, int blinkRate) throws BadLocationException {
		  textPane1.getHighlighter().addHighlight(startPos, endPos, new BlinkPainter(blinkColor, blinkRate, textPane1));
	  }
	  
	  
*/	
		
		
	private void initComponents() {
		// JFormDesigner - Component initialization - DO NOT MODIFY  //GEN-BEGIN:initComponents
		ResourceBundle bundle = ResourceBundle.getBundle("locale");
		dialogPane = new JPanel();
		contentPanel = new JPanel();
		labelLineNumber = new JLabel();
		buttonBar = new JPanel();
		this2 = new JPanel();
		tabbedPaneConcernThreshold = new JTabbedPane();
		panel1 = new JPanel();
		label1 = new JLabel();
		sliderThreshold = new JSlider();
		label2 = new JLabel();
		buttonApplyThreshold = new JButton();
		panel2 = new JPanel();
		buttonzoomOut = new JButton();
		hSpacer1 = new JPanel(null);
		buttonZoomIn = new JButton();
		panel3 = new JPanel();
		radioText = new JRadioButton();
		radioMap = new JRadioButton();
		buttonExport = new JButton();

		//======== this ========
		setVisible(true);
		setPreferredSize(new Dimension(451, 370));
		Container contentPane = getContentPane();
		contentPane.setLayout(new BorderLayout());

		//======== dialogPane ========
		{
			dialogPane.setBorder(new EmptyBorder(12, 12, 12, 12));
			dialogPane.setLayout(new BorderLayout());

			//======== contentPanel ========
			{
				contentPanel.setLayout(new BorderLayout());

				//---- labelLineNumber ----
				labelLineNumber.setText(bundle.getString("labelLineNumber.text"));
				labelLineNumber.setForeground(Color.red);
				contentPanel.add(labelLineNumber, BorderLayout.NORTH);
			}
			dialogPane.add(contentPanel, BorderLayout.CENTER);

			//======== buttonBar ========
			{
				buttonBar.setBorder(new EmptyBorder(12, 0, 0, 0));
				buttonBar.setLayout(new GridBagLayout());
				((GridBagLayout)buttonBar.getLayout()).columnWidths = new int[] {0, 0, 0, 0, 85, 85, 0};
				((GridBagLayout)buttonBar.getLayout()).columnWeights = new double[] {0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0};

				//======== this2 ========
				{
					this2.setLayout(new VerticalLayout());

					//======== tabbedPaneConcernThreshold ========
					{
						tabbedPaneConcernThreshold.setToolTipText(bundle.getString("tabbedPaneConcernThreshold.toolTipText"));
						tabbedPaneConcernThreshold.setPreferredSize(new Dimension(268, 85));

						//======== panel1 ========
						{
							panel1.setLayout(new BorderLayout());

							//---- label1 ----
							label1.setText(bundle.getString("label1.text"));
							panel1.add(label1, BorderLayout.WEST);

							//---- sliderThreshold ----
							sliderThreshold.setToolTipText(bundle.getString("sliderThreshold.toolTipText"));
							sliderThreshold.setValue(0);
							sliderThreshold.addChangeListener(new ChangeListener() {
								public void stateChanged(ChangeEvent e) {
									sliderThresholdStateChanged(e);
								}
							});
							panel1.add(sliderThreshold, BorderLayout.CENTER);

							//---- label2 ----
							label2.setText(bundle.getString("label2.text"));
							panel1.add(label2, BorderLayout.EAST);

							//---- buttonApplyThreshold ----
							buttonApplyThreshold.setText(bundle.getString("buttonApplyThreshold.text"));
							buttonApplyThreshold.addActionListener(new ActionListener() {
								public void actionPerformed(ActionEvent e) {
									buttonApplyThresholdActionPerformed(e);
								}
							});
							panel1.add(buttonApplyThreshold, BorderLayout.SOUTH);
						}
						tabbedPaneConcernThreshold.addTab(bundle.getString("panel1.tab.title"), panel1);


						//======== panel2 ========
						{
							panel2.setLayout(new FlowLayout());

							//---- buttonzoomOut ----
							buttonzoomOut.setIcon(new ImageIcon(getClass().getResource("/icons/zoom-out.jpg")));
							buttonzoomOut.setBackground(Color.white);
							buttonzoomOut.addActionListener(new ActionListener() {
								public void actionPerformed(ActionEvent e) {
									buttonzoomOutActionPerformed(e);
								}
							});
							panel2.add(buttonzoomOut);
							panel2.add(hSpacer1);

							//---- buttonZoomIn ----
							buttonZoomIn.setIcon(new ImageIcon(getClass().getResource("/icons/zoom-in.jpg")));
							buttonZoomIn.setBackground(Color.white);
							buttonZoomIn.addActionListener(new ActionListener() {
								public void actionPerformed(ActionEvent e) {
									buttonZoomInActionPerformed(e);
								}
							});
							panel2.add(buttonZoomIn);
						}
						tabbedPaneConcernThreshold.addTab(bundle.getString("panel2.tab.title"), panel2);

					}
					this2.add(tabbedPaneConcernThreshold);
				}
				buttonBar.add(this2, new GridBagConstraints(0, 0, 5, 1, 0.0, 0.0,
					GridBagConstraints.CENTER, GridBagConstraints.BOTH,
					new Insets(0, 0, 0, 5), 0, 0));

				//======== panel3 ========
				{
					panel3.setLayout(new VerticalLayout());

					//---- radioText ----
					radioText.setText(bundle.getString("radioText.text"));
					radioText.addActionListener(new ActionListener() {
						public void actionPerformed(ActionEvent e) {
							radioTextActionPerformed(e);
						}
					});
					panel3.add(radioText);

					//---- radioMap ----
					radioMap.setText(bundle.getString("radioMap.text"));
					radioMap.addActionListener(new ActionListener() {
						public void actionPerformed(ActionEvent e) {
							radioMapActionPerformed(e);
						}
					});
					panel3.add(radioMap);

					//---- buttonExport ----
					buttonExport.setText(bundle.getString("buttonExport.text"));
					panel3.add(buttonExport);
				}
				buttonBar.add(panel3, new GridBagConstraints(5, 0, 1, 1, 0.0, 0.0,
					GridBagConstraints.CENTER, GridBagConstraints.BOTH,
					new Insets(0, 0, 0, 5), 0, 0));
			}
			dialogPane.add(buttonBar, BorderLayout.SOUTH);
		}
		contentPane.add(dialogPane, BorderLayout.CENTER);

		//---- buttonGroup1 ----
		ButtonGroup buttonGroup1 = new ButtonGroup();
		buttonGroup1.add(radioText);
		buttonGroup1.add(radioMap);
		// JFormDesigner - End of component initialization  //GEN-END:initComponents
	}

	// JFormDesigner - Variables declaration - DO NOT MODIFY  //GEN-BEGIN:variables
	private JPanel dialogPane;
	private JPanel contentPanel;
	private JLabel labelLineNumber;
	private JPanel buttonBar;
	private JPanel this2;
	private JTabbedPane tabbedPaneConcernThreshold;
	private JPanel panel1;
	private JLabel label1;
	private JSlider sliderThreshold;
	private JLabel label2;
	private JButton buttonApplyThreshold;
	private JPanel panel2;
	private JButton buttonzoomOut;
	private JPanel hSpacer1;
	private JButton buttonZoomIn;
	private JPanel panel3;
	private JRadioButton radioText;
	private JRadioButton radioMap;
	private JButton buttonExport;
	// JFormDesigner - End of variables declaration  //GEN-END:variables
}
